# -*- coding: utf-8 -*-

#   This file is part of pynix.

# Copyright (c) 2009 - Fabiano Francesconi, Walter Da Col

# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:

# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
import sys

import ConfigParser
from ConfigParser import MissingSectionHeaderError

import utils
from utils import *

# exception classes
class Error(Exception):
    """Base class for cfg utils exceptions."""

    def _get_message(self):
        """Getter for 'message'; needed only to override deprecation in
        BaseException."""
        return self.__message

    def _set_message(self, value):
        """Setter for 'message'; needed only to override deprecation in
        BaseException."""
        self.__message = value

    # BaseException.message has been deprecated since Python 2.6.  To prevent
    # DeprecationWarning from popping up over this pre-existing attribute, use
    # a new property that takes lookup precedence.
    message = property(_get_message, _set_message)

    def __init__(self, msg=''):
        self.message = msg
        Exception.__init__(self, msg)

    def __repr__(self):
        return self.message

    __str__ = __repr__
    
class ConfigurationFileDoesNotExist(Error):
    """Raised when configuration file cannot be found"""

    def __init__(self, filename):
        Error.__init__(self, 'No configuration file found in: %s' % (filename, ))
        self.filename = filename

class ConfigurationFileSyntaxNotValid(Error):
    """Raised when configuration file syntax is not valid"""

    def __init__(self):
        Error.__init__(self, 'The configuration file syntax is not valid\nCheck above messages for errors')

def __check_cfg_existance(cfg_path):
    ''' checks cfg existence '''
    return utils.get_file_exists(cfg_path)

def __popolate_config(cfg_path):
    '''Popolate the configuration by reading the file located to "cfg_path"'''

    cfg_parser = ConfigParser. RawConfigParser()
    cfg = {}
    
    # load cfg
    cfg_parser.read(cfg_path)
    cfg = dict((x, dict(cfg_parser.items(x))) for x in cfg_parser.sections())

    return cfg

def check_syntax(_configuration, other_logger=None):
    '''Configuration syntax check, if verbose is true print on video. Return true if no error'''
    #TODO: Translate message
    import logging
    logger = other_logger
    
    # Note: This function is invoked during pynix normal running as well as during
    #       configuration test (-t parameter).
    #       In this second case, a logger must be forwarded to this function in order to provide
    #       the full & complete output.
    #       The following 'if statement' ensures that general logger 'Pynix Logger' is loaded only
    #       when normal program running occours.

    if (logger == None):
        logger = logging.getLogger('Pynix Logger')

    ## message example
    #logger.info(blue("* ")+_("Start configuration syntax check"))
    #logger.info(yellow(" >> ")+"bla bla something $snosn fmmks bogo")
    #logger.info(yellow(" >> ")+"bla bla something due $snosn fmmks bogo")
    #logger.info(blue("* ")+_("Configuration syntax check result:")+green(_("[ PASSED ]")))

    #Configuration Dict
    _cfg = {'General':['language'],
        'Backup':['sources', 'destination', 'exclusion_file_list'],
        'Mail':['test'],
        'Compression':['test'],
        'Log':[]}
    
    #Return Value
    no_config_error = True
    no_section_error = True

    logger.info(green("* ")+"Configuration Test"+ green(" *"))
    # Scans config for section
    for section in _cfg:
        no_section_error = True
        logger.info(green("\t"+_("%(section)s section check:" % {"section" : section})))
        if not _configuration.has_key(section):
            logger.error(red("\t\t"+_("Missing or malformed section header")))
            no_section_error = False
        else:
            #Scan section for item
            for item in _cfg[section]:
                if not item in _configuration[section]:
                    logger.error(red("\t\t"+_("Missing item: %(section)s.%(item)s" % {"section":section , "item":item})))
                    no_section_error = False
                    no_config_error = False
                elif _configuration[section][item] == '':
                    logger.error(red("\t\t"+_("Item has no value: %(section)s.%(item)s" % {"section":section , "item":item})))
                    no_section_error = False
                    no_config_error = False
        if no_section_error:
            logger.info(green("\t\t"+_("Section [OK]")))
        else:
            logger.error(red("\t\t"+_("Section [FAIL]")))

    return no_config_error

def get_configuration(cfg_path):
    ''' get configuration dictionary by reading it from cfg_path file '''
    
    # verify cfg file existance
    if not (__check_cfg_existance(cfg_path)):
        raise ConfigurationFileDoesNotExist(cfg_path)

    # popolate cfg
    cfg = __popolate_config(cfg_path)

    # check cfg syntax

    if (not check_syntax(cfg)):
        raise ConfigurationFileSyntaxNotValid()

    return cfg

def test_configuration(cfg_path):
    ''' test configuration file, show error messages and quit '''
    import logging

    def prepare_minimal_logger():
        logger = logging.getLogger('minimal logger')
        logger.setLevel(logging.DEBUG)

        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)

        # create formatter for console
        formatter = logging.Formatter("%(message)s")

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        logger.addHandler(ch)

        return logger

    # prepare a logger to print in the stdout
    logger = prepare_minimal_logger()

    # verify cfg file existance
    try:
        if not (__check_cfg_existance(cfg_path)):
            raise ConfigurationFileDoesNotExist(cfg_path)
    except ConfigurationFileDoesNotExist, error:
        logger.error(red(_("Configuration file cannot be found"))+"\n%(error)s" % {'error': error})
        sys.exit(1)


    # popolate cfg to validate the structure
    try:
        cfg = __popolate_config(cfg_path)
        #TODO: cliparser doesn't get custom cfg_path (-t option)
        print "cfg:",cfg_path
    except MissingSectionHeaderError, error:
        logger.error(red(_("A section is missing from the configuration file:"))+"\n%(error)s" % {'error': error})
        sys.exit(1)

    # check cfg syntax
    if (not check_syntax(cfg, logger)):
        #FIXME: Translate messages
        logger.error(red(_("Configuration file syntax showed some errors.\nCheck messages above for more informations.")))
        logger.error(red(_("Configuration test cannot complete itself due to the critical errors encountered.")))
        sys.exit(1)

    # quit pynix and flush log
    logging.shutdown()
    sys.exit(0)